/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-05-30     tjuye9       the first version
 */

#include "ssd1306.h"
#include "esp8266.h"

#include "hal_data.h"
#include <rtthread.h>
#include <rtdevice.h>

#include <stdio.h>

/* uart for monitor the sensor  */
#define PM_MONITOR_NAME   "uart0"

/* receive identifier */
#define START_FLAG_F       (0x42)
#define START_FLAG_S       (0x4d)

/* monitor state */
#define MONITOR_STATE_IDLE          (0x00)
#define MONITOR_STATE_GET_FLAG      (0x01)
#define MONITOR_STATE_TRANSMIT      (0x03)

/* semaphore for uart receive */
static rt_sem_t pm_monitor_sem;
/* uart device */
static rt_device_t pm_monitor_dev;
/* mutex for data sync */
static rt_mutex_t pm_mutex;
/* save the density information of particulate matters, unit ug/m3 */
rt_uint16_t pm1_den = 0, pm25_den = 0, pm10_den = 0;

static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(pm_monitor_sem);

    return RT_EOK;
}
/* data receive thread */
static void pm_monitor_thread_entry(void *parameter)
{
    rt_uint8_t ch, cnt = 0;
    /* state machine */
    rt_uint8_t curr_st = MONITOR_STATE_IDLE;

    while (1) {
        while (rt_device_read(pm_monitor_dev, -1, &ch, 1) != 1) {
            rt_sem_take(pm_monitor_sem, RT_WAITING_FOREVER);
        }

        /* lock the procedure of datas processing */
        rt_mutex_take(pm_mutex, RT_WAITING_FOREVER);
        /* the state machine is on the receiving data state */
        if (curr_st == MONITOR_STATE_TRANSMIT) {
            switch (++ cnt) {
                case(9) : pm1_den  = 0; pm1_den |= (ch << 8); break;
                case(10): pm1_den |= ch; rt_kprintf("pm1.0 density: %d ug/m3\n", pm1_den);
                          break;
                case(11): pm25_den = 0; pm25_den |= (ch << 8); break;
                case(12): pm25_den |= ch; rt_kprintf("pm2.5 density: %d ug/m3\n", pm25_den);
                          break;
                case(13): pm10_den = 0; pm10_den |= (ch << 8); break;
                case(14): pm10_den |= ch; rt_kprintf("pm1 0 density: %d ug/m3\n\n", pm10_den);
                          cnt = 0; curr_st = MONITOR_STATE_IDLE; break;
                default : break;
            }
        }
        /* unlock the procedure of datas processing */
        rt_mutex_release(pm_mutex);

        /* got the second flag, convert to next state(MONITOR_STATE_TRANSMIT) */
        if ((ch == START_FLAG_S) && (curr_st == MONITOR_STATE_GET_FLAG)) {
            curr_st = MONITOR_STATE_TRANSMIT;
            cnt = 0;
        }
        /* got the first flag, convert to next state(MONITOR_STATE_GET_FLAG) */
        if ((ch == START_FLAG_F) && (curr_st == MONITOR_STATE_IDLE)) {
            curr_st = MONITOR_STATE_GET_FLAG;
        }

        //rt_device_write(pm_monitor_dev, 0, &ch, 1);
    }
}

int pm_monitor(void)
{
    /* find the uart pmx.x monitor device */
    pm_monitor_dev = rt_device_find(PM_MONITOR_NAME);
    if (pm_monitor_dev == RT_NULL) {
        rt_kprintf("find %s failed!\n", PM_MONITOR_NAME);
        return RT_ERROR;
    }

    /* create the uart pmx.x monitor semaphore */
    pm_monitor_sem = rt_sem_create("pm_monitor_sem", 0, RT_IPC_FLAG_FIFO);
    if (pm_monitor_sem == RT_NULL) {
        rt_kprintf("create the semaphore: %s failed!\n", "pm_monitor_sem");
        return RT_ERROR;
    }

    /* open the uart pmx.x monitor device */
    rt_device_open(pm_monitor_dev, RT_DEVICE_FLAG_INT_RX);
    /* set the uart pmx.x monitor callback function */
    rt_device_set_rx_indicate(pm_monitor_dev, uart_input);

    /* create the uart pmx.x monitor thread */
    rt_thread_t tid = rt_thread_create("pm_monitor", pm_monitor_thread_entry, RT_NULL, 1024, 12, 10);
    if (tid != RT_NULL) {
        rt_thread_startup(tid);
        return RT_EOK;
    } else {
        return RT_ERROR;
    }
}
INIT_APP_EXPORT(pm_monitor);

/* data with string type */
static char pm1_0[4], pm2_5[4], pm10[4];
/* data display thread */
static void pm_display_thread_entry(void *parameter)
{
    /* initiate the oled */
    ssd1306_Init();
    rt_thread_mdelay(10);

    ssd1306_SetCursor(2, 0);
    ssd1306_WriteString("pm1.0:   ug/m3", Font_7x10, White);

    ssd1306_SetCursor(2, 20);
    ssd1306_WriteString("pm2.5:   ug/m3", Font_7x10, White);

    ssd1306_SetCursor(2, 40);
    ssd1306_WriteString("pm1 0:   ug/m3", Font_7x10, White);

    while (1) {
        /* access the data fields, lock it! */
        rt_mutex_take(pm_mutex, RT_WAITING_FOREVER);
        /* write the data to oled buffer */
        sprintf(pm1_0, "%d", pm1_den);
        sprintf(pm2_5, "%d", pm25_den);
        sprintf(pm10, "%d", pm10_den);
        /* quit the data fields, unlock it! */
        rt_mutex_release(pm_mutex);

        ssd1306_SetCursor(51, 0);
        ssd1306_WriteString(pm1_0, Font_7x10, White);
        ssd1306_SetCursor(51, 20);
        ssd1306_WriteString(pm2_5, Font_7x10, White);
        ssd1306_SetCursor(51, 40);
        ssd1306_WriteString(pm10, Font_7x10, White);
        /* refresh the oled */
        ssd1306_UpdateScreen();
        rt_thread_mdelay(1000);
    }
}

int pm_display(void)
{
    /* create the mutex lock */
    pm_mutex = rt_mutex_create("pm_dis", RT_IPC_FLAG_FIFO);
    if (pm_mutex == RT_NULL) {
        rt_kprintf("create the mutex: %s failed!\n", "pm_mutex");
        return RT_ERROR;
    }

    /* create the data display thread */
    rt_thread_t tid = rt_thread_create("pm_display", pm_display_thread_entry, RT_NULL, 1024, 16, 10);
    if (tid != RT_NULL) {
        rt_thread_startup(tid);
        return RT_EOK;
    } else {
        return RT_ERROR;
    }
}
INIT_APP_EXPORT(pm_display);

/* data transfer thread */
static void pm_data_transfer_thread_entry(void *parameter)
{
    char pm1_0_sendbuf[] = "pm1.0 density: xx ug/m3\n";
    char pm2_5_sendbuf[] = "pm2.5 density: xx ug/m3\n";
    char pm10_sendbuf[]  = "pm1 0 density: xx ug/m3\n";

    while (1) {
        pm1_0_sendbuf[15] = pm1_0[0];
        pm1_0_sendbuf[16] = pm1_0[1];
        esp8266_send_data(pm1_0_sendbuf);

        pm2_5_sendbuf[15] = pm2_5[0];
        pm2_5_sendbuf[16] = pm2_5[1];
        esp8266_send_data(pm2_5_sendbuf);

        pm10_sendbuf[15]  = pm10[0];
        pm10_sendbuf[16]  = pm10[1];
        esp8266_send_data(pm10_sendbuf);
        rt_thread_mdelay(1000);
    }
}

int pm_data_transfer(void)
{
    /* initiate the net configuration */
    esp8266_init();

    /* create the data transfer thread */
    rt_thread_t tid = rt_thread_create("pm_data_transfer", pm_data_transfer_thread_entry, RT_NULL, 512 * 3, 18, 10);
    if (tid != RT_NULL) {
        rt_thread_startup(tid);
        return RT_EOK;
    } else {
        return RT_ERROR;
    }
}
INIT_APP_EXPORT(pm_data_transfer);
